package com.numerikdjs.dao.jpa;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;

import org.apache.juli.logging.LogFactory;

import com.numerikdjs.dao.GenericDAO;

public class GenericDAOJPA<T> implements GenericDAO<T> {

	protected final static String PORCENTAJE = "%";

	@PersistenceContext(unitName = "NumerikDjs-PU")
	private EntityManager em;
	private Class<T> persistentClass;

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public GenericDAOJPA() {
		Type t = getClass().getGenericSuperclass();
		Type arg = null;
		if (t instanceof ParameterizedType) {
			arg = ((ParameterizedType) t).getActualTypeArguments()[0];
		} else if (t instanceof Class) {
			arg = ((ParameterizedType) ((Class) t).getGenericSuperclass()).getActualTypeArguments()[0];
		}
		this.persistentClass = (Class<T>) arg;
	}

	public Class<T> getPersistentClass() {
		return persistentClass;
	}

	protected EntityManager getEntityManager() {
		try {
			return em;

		} catch (Exception e) {
			LogFactory.getLog(getClass()).error("Error al obtener el EntityManager", e);
			throw new RuntimeException(e);
		}
	}

	@Override
	public void create(T entity) {
		getEntityManager().persist(entity);
		getEntityManager().flush();
	}

	@Override
	public T edit(T entity) {
		T result = getEntityManager().merge(entity);
		getEntityManager().flush();
		return result;
	}

	@Override
	public void remove(T entity) {
		getEntityManager().remove(getEntityManager().merge(entity));
		getEntityManager().flush();
	}

	@Override
	public T find(Object id) {
		return getEntityManager().find(this.persistentClass, id);
	}

	@Override
	@SuppressWarnings(value = { "unchecked", "rawtypes" })
	public List<T> findAll() {
		CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
		cq.select(cq.from(this.persistentClass));
		return getEntityManager().createQuery(cq).getResultList();
	}

	@Override
	@SuppressWarnings(value = { "unchecked", "rawtypes" })
	public List<T> findRange(int[] range) {
		CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
		cq.select(cq.from(this.persistentClass));
		Query q = getEntityManager().createQuery(cq);
		q.setMaxResults(range[1] - range[0]);
		q.setFirstResult(range[0]);
		return q.getResultList();
	}

	@Override
	@SuppressWarnings(value = { "unchecked", "rawtypes" })
	public int count() {
		CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
		Root<T> rt = cq.from(this.persistentClass);
		cq.select(getEntityManager().getCriteriaBuilder().count(rt));
		Query q = getEntityManager().createQuery(cq);
		return ((Long) q.getSingleResult()).intValue();
	}

	@Override
	public void refresh(T entity) {
		getEntityManager().refresh(entity);
	}

	protected EntityType<T> getEntityType() {
		Metamodel m = getEntityManager().getMetamodel();
		return m.entity(this.persistentClass);
	}
}
